//    Copyright (c) 2009 Elwyn Malethan
//
//    This file is part of SeemoreJ.
//
//    SeemoreJ is free software: you can redistribute it and/or modify
//    it under the terms of the GNU General Public License as published by
//    the Free Software Foundation, either version 3 of the License, or
//    (at your option) any later version.
//
//    SeemoreJ is distributed in the hope that it will be useful,
//    but WITHOUT ANY WARRANTY; without even the implied warranty of
//    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//    GNU General Public License for more details.
//
//    You should have received a copy of the GNU General Public License
//    along with SeemoreJ.  If not, see <http://www.gnu.org/licenses/>.

package com.malethan.seemorej.hibernate;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.hibernate.HibernateException;
import org.hibernate.Interceptor;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.cfg.AnnotationConfiguration;
import org.hibernate.cfg.Configuration;

/**
 * <p>A convenience class for using the Hibernate API</p>
 */
public class HibernateUtil {
    //----------------------------------------------------------------------- Static Properties and Constants

    private static final Log log = LogFactory.getLog(HibernateUtil.class);
    private static final ThreadLocal<Session> THREAD_SESSION = new ThreadLocal<Session>();
    private static final ThreadLocal<Interceptor> THREAD_INTERCEPTOR = new ThreadLocal<Interceptor>();
    private static SessionFactory sessionFactory;
    private static Configuration configuration;

    //----------------------------------------------------------------------- Static Methods

    /**
     * <p>Returns a hibernate session as specified by the session factory being used. There can be only one session
     * open at a time for any given thread.</p>
     *
     * @return the current Hibernate session
     */
    public static synchronized Session currentSession() {
        return THREAD_SESSION.get();
    }

    public static synchronized void openSession() throws HibernateException {
        if (log.isDebugEnabled()) {
            log.debug("About to open a hibernate session");
        }

        //make sure that there is a session factory
        getSessionFactory();

        //we only open a new session if there isn't one for this thread...
        if (currentSession() == null) {
            THREAD_SESSION.set(sessionFactory.openSession());
        }
    }

    /**
     * Closes the hibernate session currently in use
     *
     * @throws HibernateException
     */
    public static void closeSession() throws HibernateException {
        Session s = THREAD_SESSION.get();
        THREAD_SESSION.set(null);
        if (s != null) {
            s.close();
        }
    }

    /**
     * Allows an alternative session factory to be specified, useful for unit testing
     *
     * @param factory
     */
    public static void setSessionFactory(SessionFactory factory) throws HibernateException {
        closeSession();
        sessionFactory = factory;
    }

    /**
     * Allows access to the session factory for creation separate Hibernate sessions
     *
     * @return
     */
    public static SessionFactory getSessionFactory() {
        //If there is no session factory set up then instantiate the default
        if (sessionFactory == null) {
            // Create the ConnectionFactory
            if (log.isDebugEnabled()) {
                log.debug("The this.sessionFactory is null, we'll create a new one with the default configuration");
            }
            configuration = new AnnotationConfiguration().configure();
            if(THREAD_INTERCEPTOR.get() != null) {
                log.debug("Using interceptor");
                configuration.setInterceptor(THREAD_INTERCEPTOR.get());
            }
            sessionFactory = configuration.buildSessionFactory();
        }
        return sessionFactory;
    }

    public static Configuration getConfiguration() {
        return configuration;
    }

    public static void registerInterceptor(Interceptor interceptor) {
        THREAD_INTERCEPTOR.set(interceptor);
    }
}
